# This is a BitKeeper generated patch for the following project: # Project Name: Linux kernel tree # This patch format is intended for GNU patch command version 2.5 or higher. # This patch includes the following deltas: # ChangeSet 1.1091 -> 1.1092 # include/linux/timex.h 1.6 -> 1.7 # kernel/time.c 1.13 -> 1.14 # kernel/timer.c 1.54 -> 1.55 # # The following is the BitKeeper ChangeSet Log # -------------------------------------------- # 03/05/19 jbarnes@sgi.com 1.1079.1.20 # [PATCH] ia64: SN cleanups # # Some patches from hch@infradead.org cleaning up sn2 code a bit, # including the removal of some unnecessary files. # -------------------------------------------- # 03/05/19 davidm@tiger.hpl.hp.com 1.1092 # Overhaul time-interpolation support. Platforms that do not ever wish to # use time-interpolation should NOT define CONFIG_TIME_INTERPOLATION. # There will be no overhead on such platforms. # # If you have a platform that has exactly one time-interpolation source, # define CONFIG_TIME_INTERPOLATION and let the generic code take care # of updating "last_nsec_offset" as needed, but never call # register_time_interpolator(). Instead, hard-code the updating of # last_nsec_offset in do_gettimeofday() (and do_settimeofday()). # This will minimize overhead to a single load & NULL check (everything else # gets inlined). # # If you have a platform that needs to support multiple interpolation # sources, register each source with regsiter_time_interpolator(). # Each call to time_interpolator_update()/time_interpolator_reset() will # then incur an indirect function call. C'est la vie. # -------------------------------------------- # diff -Nru a/include/linux/timex.h b/include/linux/timex.h --- a/include/linux/timex.h Tue May 20 00:03:03 2003 +++ b/include/linux/timex.h Tue May 20 00:03:03 2003 @@ -51,6 +51,9 @@ #ifndef _LINUX_TIMEX_H #define _LINUX_TIMEX_H +#include +#include + #include /* @@ -310,12 +313,103 @@ extern long pps_errcnt; /* calibration errors */ extern long pps_stbcnt; /* stability limit exceeded */ -/* - * Call-back for high precision timer sources to snapshot every time - * wall_jiffies is updated. - */ -extern void (*update_wall_time_hook)(long adjust_nsec); -extern void (*reset_wall_time_hook)(void); +#ifdef CONFIG_TIME_INTERPOLATION + +struct time_interpolator { + /* cache-hot stuff first: */ + unsigned long (*get_offset) (void); + void (*update) (long); + void (*reset) (void); + + /* cache-cold stuff follows here: */ + struct time_interpolator *next; + unsigned long frequency; /* frequency in counts/second */ + long drift; /* drift in parts-per-million (or -1) */ +}; + +extern volatile unsigned long last_nsec_offset; +#ifndef __HAVE_ARCH_CMPXCHG +extern spin_lock_t last_nsec_offset_lock; +#endif +extern struct time_interpolator *time_interpolator; + +extern void register_time_interpolator (struct time_interpolator *); +extern void unregister_time_interpolator (struct time_interpolator *); + +/* Called with xtime read- OR write-lock acquired. */ +static inline void +time_interpolator_update (long delta_nsec) +{ + struct time_interpolator *ti = time_interpolator; + + if (last_nsec_offset > 0) { +#ifdef __HAVE_ARCH_CMPXCHG + unsigned long new, old; + + do { + old = last_nsec_offset; + if (old > delta_nsec) + new = old - delta_nsec; + else + new = 0; + } while (cmpxchg(&last_nsec_offset, old, new) != old); +#else + /* + * This really hurts, because it serializes gettimeofday(), but without an + * atomic single-word compare-and-exchange, there isn't all that much else + * we can do. + */ + spin_lock(&last_nsec_offset_lock); + { + last_nsec_offset -= min(last_nsec_offset, delta_nsec); + } + spin_unlock(&last_nsec_offset_lock); +#endif + } + + if (ti) + (*ti->update)(delta_nsec); +} + +/* Called with xtime read- or write-lock acquired. */ +static inline void +time_interpolator_reset (void) +{ + struct time_interpolator *ti = time_interpolator; + + last_nsec_offset = 0; + if (ti) + (*ti->reset)(); +} + +static inline unsigned long +time_interpolator_get_offset (void) +{ + struct time_interpolator *ti = time_interpolator; + if (ti) + return (*ti->get_offset)(); + return last_nsec_offset; +} + +#else /* !CONFIG_TIME_INTERPOLATION */ + +static inline void +time_interpolator_update (long delta_nsec) +{ +} + +static inline void +time_interpolator_reset (void) +{ +} + +static inline unsigned long +time_interpolator_get_offset (void) +{ + return 0; +} + +#endif /* !CONFIG_TIME_INTERPOLATION */ #endif /* KERNEL */ diff -Nru a/kernel/time.c b/kernel/time.c --- a/kernel/time.c Tue May 20 00:03:03 2003 +++ b/kernel/time.c Tue May 20 00:03:03 2003 @@ -76,7 +76,7 @@ return -EFAULT; write_seqlock_irq(&xtime_lock); - (*reset_wall_time_hook)(); + time_interpolator_reset(); xtime.tv_sec = value; xtime.tv_nsec = 0; time_adjust = 0; /* stop active adjtime() */ @@ -124,7 +124,7 @@ { write_seqlock_irq(&xtime_lock); xtime.tv_sec += sys_tz.tz_minuteswest * 60; - (*update_wall_time_hook)(sys_tz.tz_minuteswest * 60 * NSEC_PER_SEC); + time_interpolator_update(sys_tz.tz_minuteswest * 60 * NSEC_PER_SEC); write_sequnlock_irq(&xtime_lock); } diff -Nru a/kernel/timer.c b/kernel/timer.c --- a/kernel/timer.c Tue May 20 00:03:03 2003 +++ b/kernel/timer.c Tue May 20 00:03:03 2003 @@ -65,16 +65,6 @@ typedef struct tvec_t_base_s tvec_base_t; -/* - * Hooks for using external high precision timers for the system clock. - * On systems where the CPU clock isn't synchronized between CPUs, - * it is necessary to use an external source such as an RTC to obtain - * precision in gettimeofday(). - */ -static void nop (void) { } -void (*update_wall_time_hook)(long delta_nsec) = (void (*) (void)) &nop; -void (*reset_wall_time_hook)(void) = &nop; - static inline void set_running_timer(tvec_base_t *base, struct timer_list *timer) { @@ -527,7 +517,7 @@ if (xtime.tv_sec % 86400 == 0) { xtime.tv_sec--; wall_to_monotonic.tv_sec++; - (*update_wall_time_hook)(-NSEC_PER_SEC); + time_interpolator_update(-NSEC_PER_SEC); time_state = TIME_OOP; clock_was_set(); printk(KERN_NOTICE "Clock: inserting leap second 23:59:60 UTC\n"); @@ -538,7 +528,7 @@ if ((xtime.tv_sec + 1) % 86400 == 0) { xtime.tv_sec++; wall_to_monotonic.tv_sec--; - (*update_wall_time_hook)(NSEC_PER_SEC); + time_interpolator_update(NSEC_PER_SEC); time_state = TIME_WAIT; clock_was_set(); printk(KERN_NOTICE "Clock: deleting leap second 23:59:59 UTC\n"); @@ -654,7 +644,7 @@ delta_nsec += ltemp; } xtime.tv_nsec += delta_nsec; - (*update_wall_time_hook)(delta_nsec); /* update time interpolation */ + time_interpolator_update(delta_nsec); } /* @@ -674,7 +664,7 @@ if (xtime.tv_nsec >= 1000000000) { xtime.tv_nsec -= 1000000000; xtime.tv_sec++; - (*update_wall_time_hook)(NSEC_PER_SEC); + time_interpolator_update(NSEC_PER_SEC); second_overflow(); } } @@ -1234,3 +1224,80 @@ register_cpu_notifier(&timers_nb); open_softirq(TIMER_SOFTIRQ, run_timer_softirq, NULL); } + +#ifdef CONFIG_TIME_INTERPOLATION + +volatile unsigned long last_nsec_offset; + +struct time_interpolator *time_interpolator; + +#ifndef __HAVE_ARCH_CMPXCHG +spinlock_t last_nsec_offset_lock = SPIN_LOCK_UNLOCKED; +#endif + +static struct { + spinlock_t lock; /* lock protecting list */ + struct time_interpolator *list; /* list of registered interpolators */ +} ti_global = { + .lock = SPIN_LOCK_UNLOCKED +}; + +static inline int +is_better_time_interpolator (struct time_interpolator *new) +{ + if (!time_interpolator) + return 1; + return new->frequency > 2*time_interpolator->frequency + || (unsigned long) new->drift < (unsigned long) time_interpolator->drift; +} + +void +register_time_interpolator (struct time_interpolator *ti) +{ + spin_lock(&ti_global.lock); + { + write_seqlock_irq(&xtime_lock); + { + if (is_better_time_interpolator(ti)) + time_interpolator = ti; + } + write_sequnlock_irq(&xtime_lock); + + ti->next = ti_global.list; + ti_global.list = ti; + } + spin_unlock(&ti_global.lock); +} + +void +unregister_time_interpolator (struct time_interpolator *ti) +{ + struct time_interpolator *curr, **prev; + + spin_lock(&ti_global.lock); + { + prev = &ti_global.list; + for (curr = *prev; curr; curr = curr->next) { + if (curr == ti) { + *prev = curr->next; + break; + } + prev = &curr->next; + } + write_seqlock_irq(&xtime_lock); + { + if (ti == time_interpolator) { + /* we lost the best time-interpolator: */ + time_interpolator = NULL; + /* find the next-best interpolator */ + for (curr = ti_global.list; curr; curr = curr->next) + if (is_better_time_interpolator(curr)) + time_interpolator = curr; + } + } + write_sequnlock_irq(&xtime_lock); + } + spin_unlock(&ti_global.lock); +} + +#endif /* CONFIG_TIME_INTERPOLATION */